#pragma once

#include<iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include<sys/msg.h>
#include<string>
#include<cstring>

//默认键值和大小
const int gdefaultfd = -1;
const int gdefaultsize = 1024;

//创建唯一键值的参数
#define PATHNAME "/home/lhx/linux_code/112/message_queue"
#define PROJID 0x123

//创建消息队列的参数
#define Get_Msgq (IPC_CREAT)
#define CREATE_MSGQ (IPC_CREAT | IPC_EXCL | 0666)

class Messagequeue
{
    struct msgbuf
    {
        long type;
        char mtext[gdefaultsize];
    };
public:
    Messagequeue():_msgfd(gdefaultfd)
    {}

    //创建消息队列
    void Create(int flag)
    {
        //获取唯一键值
        //key_t ftok(const char *pathname, int proj_id);
        key_t key = ftok(PATHNAME, PROJID);
        if(key == -1)
        {
            std::cerr << "ftok error" << std::endl;
            exit(1);
        }
        //创建消息队列
        //int msgget(key_t key, int msgflg);
        //flag对于客户端和服务器是不同的：
        // 客户端只获取，服务器则创建新的，有旧的就返回错误，没有则创建，并赋予权限
        _msgfd = msgget(key, flag);
        if(_msgfd == -1)
        {
            std::cerr << "msgget error" << std::endl;
            exit(2);
        }
        std::cout << "msgqueue created: " << std::hex << _msgfd << std::endl;

    }

    //收发消息（都要创建msgbuf结构体进行消息收发）
    //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    //发送消息
    //输入型参数
    void Send(int type,const std::string &text)
    {
        //创建msgbuf对象并清零
        struct msgbuf mbf;
        memset(&mbf,0,sizeof(mbf));
        //填充buf信息
        mbf.type = type;
        memcpy(mbf.mtext,text.c_str(),sizeof(text));
        //发送
        //发送的大小不包括type，只计算mtext的大小
        int n = msgsnd(_msgfd, &mbf, sizeof(mbf.mtext), 0);
        if(n == - 1)
        {
            std::cerr << "msgsnd error" << std::endl;
            exit(3);
        }
    }
    //接收消息
    //输出型参数
    void Recv(int type,std::string &text)
    {
        //创建buf对象不清零
        struct msgbuf mbf;
        memset(&mbf, 0, sizeof(mbf));
        //收消息
        //On failure both functions return -1 with errno indicating the error, 
        // otherwise msgsnd() returns 0 and msgrcv() returns the number of bytes actually copiedinto the mtext array.
        int n = msgrcv(_msgfd, &mbf, sizeof(mbf.mtext), type, 0);
        if(n == -1)
        {
            std::cerr << "msgrcv error" << std::endl;
            exit(4);
        }
        //补充buf信息
        mbf.mtext[n] = '\0';//使其成为完整的字符串
        //输出参数
        text = mbf.mtext;
    }
    
    //获取消息队列内容属性
    void GetArr()
    {
        struct msqid_ds outbuffer;

        int n = ::msgctl(_msgfd,IPC_STAT,&outbuffer);
        if(n == -1)
        {
            std::cout << "GetArr error" << std::endl;
            return;
        }

        std::cout << "msg_perm__key: " << std::hex << outbuffer.msg_perm.__key << std::endl;
    }

    //销毁消息队列
    void Destory()
    {
        int n = msgctl(_msgfd, IPC_RMID, 0);
        if(n == -1)
        {
            std::cerr << "msgctl error" << std::endl;
            exit(3);
        }
        std::cout << "msgqueue destotyed" << std::endl;
    }
    //删除消息队列
    ~Messagequeue()
    {}
private:
    int _msgfd;
};

//定义消息队列类型
#define MSG_TYPE_CLIENT 1
#define MSG_TYPE_SERVER 2

class Server:public Messagequeue
{
public:
    Server()
    {
        Messagequeue::Create(CREATE_MSGQ);
        std::cout << "server create msgqueue done" << std::endl;
        Messagequeue::GetArr();
    }
    ~Server()
    {
        Messagequeue::Destory();
    }
};

class Client:public Messagequeue
{
public:
    Client()
    {
        Messagequeue::Create(Get_Msgq);
        std::cout << "clinet create msgqueue done" << std::endl;
    }
    ~Client()
    {}
};